/*
    Movino-S60
    Copyright 2006, 2007 Martin Storsj�

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as
    published by the Free Software Foundation; either version 2.1 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include "jpegencoder.h"
#include <stdio.h>
#include <stdlib.h>

void CJpegEncoder::error_exit(j_common_ptr cinfo) {
	CJpegEncoder* data = (CJpegEncoder*) cinfo->client_data;

	cinfo->err->format_message(cinfo, data->iMessageBuffer);
/*
	TBuf<300> buf;
	char *ptr = data->iMessageBuffer;
	while (*ptr)
		buf.Append(*ptr++);
	User::Panic(buf, 42);
*/
	longjmp(data->iReturnPoint, 1);
}

void CJpegEncoder::output_message(j_common_ptr cinfo) {
/*
	printf("Outputting message:\n");
	char buf[JMSG_LENGTH_MAX+1];
	cinfo->err->format_message(cinfo, buf);
	printf("%s\n", buf);
*/
}

void CJpegEncoder::init_destination(j_compress_ptr cinfo) {
	CJpegEncoder* data = (CJpegEncoder*) cinfo->client_data;
	cinfo->dest->next_output_byte = data->iWriteBuffer;
	cinfo->dest->free_in_buffer = data->iWriteBufferSize;
}

boolean CJpegEncoder::empty_output_buffer(j_compress_ptr cinfo) {

	CJpegEncoder* data = (CJpegEncoder*) cinfo->client_data;
/*
//	data->iWriter->Send(TPtrC8(data->iWriteBuffer, sizeof(data->iWriteBuffer)));

	cinfo->dest->next_output_byte = data->iWriteBuffer;
	cinfo->dest->free_in_buffer = sizeof(data->iWriteBuffer);
	// ERREXIT(cinfo, JERR_FILE_WRITE);
*/
//	ERREXIT(cinfo, JERR_FILE_WRITE);

	TInt pos = data->iWriteBufferSize;

	data->iWriteBufferSize *= 2;
	TUint8* newBuf = (TUint8*) realloc(data->iWriteBuffer, data->iWriteBufferSize);

	if (!newBuf)
		ERREXIT(cinfo, JERR_FILE_WRITE);
	data->iWriteBuffer = newBuf;

	cinfo->dest->next_output_byte = data->iWriteBuffer + pos;
	cinfo->dest->free_in_buffer = data->iWriteBufferSize - pos;
	return TRUE;
}

void CJpegEncoder::term_destination(j_compress_ptr cinfo) {
	CJpegEncoder* data = (CJpegEncoder*) cinfo->client_data;
	data->iWritten = data->iWriteBufferSize - cinfo->dest->free_in_buffer;
//	if (size > 0)
//		data->iWriter->Send(TPtrC8(data->iWriteBuffer, size));
}

TDesC& CJpegEncoder::GetErrorString() {
	iMessageDesc.Zero();
	char *ptr = iMessageBuffer;
	while (*ptr)
		iMessageDesc.Append(*ptr++);
	return iMessageDesc;
}

CJpegEncoder::CJpegEncoder(TInt aQuality) {
	iPlaneHeight = 0;
	iMessageBuffer[0] = '\0';

	iWriteBufferSize = 50000;
	iWriteBuffer = (TUint8*) malloc(iWriteBufferSize);

	iDestMgr.init_destination = init_destination;
	iDestMgr.empty_output_buffer = empty_output_buffer;
	iDestMgr.term_destination = term_destination;

	iCinfo.client_data = (void*) this;

	iCinfo.err = jpeg_std_error(&iJerr);
	iJerr.error_exit = error_exit;
	iJerr.output_message = output_message;

	jpeg_create_compress(&iCinfo);

	iCinfo.dest = &iDestMgr;


	iCinfo.input_components = 3;
	iCinfo.in_color_space = JCS_YCbCr;

	jpeg_set_defaults(&iCinfo);

	iCinfo.dct_method = JDCT_FASTEST;
	iCinfo.optimize_coding = FALSE;

	iCinfo.raw_data_in = TRUE;

	jpeg_set_colorspace(&iCinfo, JCS_YCbCr);

	SetQuality(aQuality);
}

CJpegEncoder::~CJpegEncoder() {
	jpeg_destroy_compress(&iCinfo);
	free(iYPlane);
	free(iCbPlane);
	free(iCrPlane);
	free(iWriteBuffer);
}

TDesC8& CJpegEncoder::SetQuality(TInt aQuality) {
	if (setjmp(iReturnPoint)) {
//		printf("Error: %s\n", iMessageBuffer);
		return iEmptyDesc;
	}

	jpeg_set_quality(&iCinfo, aQuality, FALSE);
	jpeg_suppress_tables(&iCinfo, FALSE);
	jpeg_write_tables(&iCinfo);

	iDataDesc.Set(iWriteBuffer, iWritten);
	return iDataDesc;
}

TDesC8& CJpegEncoder::Encode(TDesC8& aData, TInt aWidth, TInt aHeight) {
	if (iPlaneHeight < aHeight || iYPlane == NULL) {
		free(iYPlane);
		free(iCbPlane);
		free(iCrPlane);
		iYPlane = (JSAMPARRAY) malloc(sizeof(JSAMPROW)*aHeight);
		iCbPlane = (JSAMPARRAY) malloc(sizeof(JSAMPROW)*(aHeight/2));
		iCrPlane = (JSAMPARRAY) malloc(sizeof(JSAMPROW)*(aHeight/2));
		iPlaneHeight = aHeight;
	}

	if (setjmp(iReturnPoint)) {
//		printf("Error: %s\n", iMessageBuffer);
		return iEmptyDesc;
	}


	iCinfo.image_width = aWidth;
	iCinfo.image_height = aHeight;

	// needed since the libjpeg typedefs don't include const as needed
	TUint8* frame = const_cast<TUint8*>(aData.Ptr());

	iCinfo.comp_info[0].h_samp_factor = iCinfo.comp_info[0].v_samp_factor = 2;
	iCinfo.comp_info[1].h_samp_factor = iCinfo.comp_info[1].v_samp_factor = 1;
	iCinfo.comp_info[2].h_samp_factor = iCinfo.comp_info[2].v_samp_factor = 1;
	for (TInt i = 0; i < aHeight; i++)
		iYPlane[i] = &frame[i*aWidth];
	for (TInt i = 0; i < aHeight/2; i++) {
		iCbPlane[i] = &frame[aWidth*aHeight + i*(aWidth/2)];
		iCrPlane[i] = &frame[aWidth*aHeight + (aWidth/2)*(aHeight/2) + i*(aWidth/2)];
	}

	jpeg_start_compress(&iCinfo, FALSE);

//	jpeg_start_compress(&iCinfo, TRUE);

	TInt y = 0;
	TInt blockrow = 0;
	while (y < aHeight) {
		JSAMPARRAY planes[3];
		planes[0] = iYPlane + iCinfo.comp_info[0].v_samp_factor*DCTSIZE*blockrow;
		planes[1] = iCbPlane + iCinfo.comp_info[1].v_samp_factor*DCTSIZE*blockrow;
		planes[2] = iCrPlane + iCinfo.comp_info[2].v_samp_factor*DCTSIZE*blockrow;
		jpeg_write_raw_data(&iCinfo, planes, iCinfo.comp_info[0].v_samp_factor*DCTSIZE);
		y += iCinfo.comp_info[0].v_samp_factor*DCTSIZE;
		blockrow++;
	}

	jpeg_finish_compress(&iCinfo);

	iDataDesc.Set(iWriteBuffer, iWritten);
	return iDataDesc;
}


